export interface PointType {
    x: number;
    y: number;
}

/**获取切线角度（东为0度，逆时针递增）
 * @param start 起点
 * @param end 终点
 * @returns 角度
 */
export const getRoadAngle = (start: { x: number, y: number }, end: { x: number, y: number }) => {
    const angle: number = Math.atan2(end.x - start.x, end.y - start.y);
    const theta: number = 180 - angle * (180 / Math.PI);
    return theta - 90;
};


/**获取三阶贝塞尔曲线上的任意一点坐标
 * @param t 进度/比例
 * @param p1 起点
 * @param cp1 控制点1
 * @param cp2 控制点2
 * @param p2 终点
 * @returns 坐标
 */
export const threeOrderBezier = (
    t: number,
    p1: { x: number; y: number },
    cp1: [number, number],
    cp2: [number, number],
    p2: { x: number; y: number }
) => {
    const { x: x1, y: y1 } = p1,
        [cx1, cy1] = cp1,
        [cx2, cy2] = cp2,
        { x: x2, y: y2 } = p2;
    const x =
        x1 * (1 - t) * (1 - t) * (1 - t) +
        3 * cx1 * t * (1 - t) * (1 - t) +
        3 * cx2 * t * t * (1 - t) +
        x2 * t * t * t;
    const y =
        y1 * (1 - t) * (1 - t) * (1 - t) +
        3 * cy1 * t * (1 - t) * (1 - t) +
        3 * cy2 * t * t * (1 - t) +
        y2 * t * t * t;
    return {x, y};
};


/**格式化坐标值（n位） */
const formatValue = (value: number, n: number) => Math.round(value * Math.pow(10, n)) / Math.pow(10, n);

/**
 * 旋转点到新位置
 * @param origin 原始点
 * @param around 围绕点
 * @param angle 角度
 * @returns 新位置
 */
export const rotateToNewPositionByAngle = (
    origin: { x: number; y: number },
    around: { x: number; y: number },
    angle: number,
    n ?: number // 保留位数
) => {
    let n_ = n ?? 3;
    const anglePI = (angle / 180) * Math.PI;
    const x = formatValue(
        (origin.x - around.x) * Math.cos(anglePI) -
        (origin.y - around.y) * Math.sin(anglePI) +
        around.x, n_
    );
    const y = formatValue(
        (origin.y - around.y) * Math.cos(anglePI) +
        (origin.x - around.x) * Math.sin(anglePI) +
        around.y, n_
    );
    return { x, y };
};

/**
 * 某个点转换到某个坐标系的坐标
 * @param origin axios原始点
 * @param axiosAngle axios角度
 * @param target 目标点
 * @returns 目标点相对新的坐标系的坐标
 */
export const conversionToAxiosPosition = (
    origin: { x: number; y: number },
    axiosAngle: number,
    target: { x: number; y: number },
) => {
    const len = Math.sqrt(
        Math.pow(origin.x - target.x, 2) + Math.pow(origin.y - target.y, 2)
    );
    const angle = getRoadAngle(origin, target) - axiosAngle;
    const anglePI = (angle / 180) * Math.PI;
    const x = len * Math.cos(anglePI);
    const y = len * Math.sin(anglePI);
    return { x, y };
};

/**
 * 计算两条线段的交点
 * @param l1 线段1
 * @param l2 线段2
 * @returns 交点坐标
 */
export const getLineSegIntersection = ( 
    l1: {
        start: PointType,
        end: PointType,
    },
    l2: {
        start: PointType,
        end: PointType,
    },
): PointType | null => {
    //线段ab的法线N1
    const nx1 = l1.end.y - l1.start.y,
        ny1 = l1.start.x - l1.end.x;

    //线段cd的法线N2
    const nx2 = l2.end.y - l2.start.y,
        ny2 = l2.start.x - l2.end.x;

    //两条法线做叉乘, 如果结果为0, 说明线段ab和线段cd平行或共线,不相交
    const denominator = nx1 * ny2 - ny1 * nx2;
    if (denominator == 0) {
        return null;
    }

    //在法线N2上的投影
    const distC_N2 = nx2 * l2.start.x + ny2 * l2.start.y;
    const distA_N2 = nx2 * l1.start.x + ny2 * l1.start.y - distC_N2;
    const distB_N2 = nx2 * l1.end.x + ny2 * l1.end.y - distC_N2;

    // 点a投影和点b投影在点c投影同侧 (对点在线段上的情况,本例当作不相交处理);
    if (distA_N2 * distB_N2 >= 0) {
        return null;
    }

    //判断点c点d 和线段ab的关系, 原理同上
    //在法线N1上的投影
    const distA_N1 = nx1 * l1.start.x + ny1 * l1.start.y;
    const distC_N1 = nx1 * l2.start.x + ny1 * l2.start.y - distA_N1;
    const distD_N1 = nx1 * l2.end.x + ny1 * l2.end.y - distA_N1;
    if (distC_N1 * distD_N1 >= 0) {
        return null;
    }

    //计算交点坐标
    const fraction = distA_N2 / denominator;
    const dx = fraction * ny1,
        dy = -fraction * nx1;
    return { x: l1.start.x + dx, y: l1.start.y + dy };
};

/**
 * 计算两条线段的交点
 * @param line1 线段1
 * @param line2 线段2
 * @param options 选项 noVertex 不包含顶点 decimalPlaces 保留位数
 * @returns 交点坐标
 */
export const getIntersectionPoint = (
    line1: { p1: PointType, p2: PointType }, 
    line2: { p1: PointType, p2: PointType }, 
    options?: {
        noVertex?: boolean; // 不包含顶点,
        decimalPlaces?: number; // 保留位数
    }
) => {
  const n_ = options?.decimalPlaces ?? 3;
  const { x: x1, y: y1 } = line1.p1;
  const { x: x2, y: y2 } = line1.p2;
  const { x: x3, y: y3 } = line2.p1;
  const { x: x4, y: y4 } = line2.p2;

  // 计算分母
  const denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);

  // 如果分母为0，两条直线平行或重合，没有交点
  if (denominator === 0) {
    return null;
  }
  // 计算分子
  const tNumerator = (x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4);
  const uNumerator = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3));

  // 计算参数t和u（范围0-1表示在对应线段上）
  const t = formatValue(tNumerator / denominator, n_);
  const u = formatValue(uNumerator / denominator, n_);

  // 检查交点是否同时在两条线段上
  if (t >= 0 && t <= 1 && u >= 0 && u <= 1) {
    // 计算交点坐标
    const x = formatValue(x1 + t * (x2 - x1), n_);
    const y = formatValue(y1 + t * (y2 - y1), n_);

    // console.log('交点坐标：', x3, y3, x4, y4, x, y);
    // console.log('noVertex', noVertex);
    // console.log('flag', noVertex && ((x === x3 && y === y3) || (x === x4 && y === y4)));
    if (options?.noVertex && ((x === x3 && y === y3) || (x === x4 && y === y4))) {
      return null;
    }
    return { x, y };
  }
  return null;
}